package asteroids.model;

import java.util.*;
import java.lang.Math;

import be.kuleuven.cs.som.annotate.*;
import asteroids.CollisionListener;

/**
 * A class of asteroids.
 * 
 * @invar	...
 * 			| density == 2.65*10^12
 *
 * @version 1.0
 * 
 * @author	Thijs Peirelinck & Wouter Parys
 *
 *
 */
public class Asteroid extends FlyingObject{
	
	private static final double density = 2.65*Math.pow(10,12);
	private Random random;

	/**
	 * Constructor for the class asteroid
	 * 
	 * @param positionX
	 * @param positionY
	 * @param velocityX
	 * @param velocityY
	 * @param radius
	 * 
	 * @effect	...
	 * 			|super(positionX,positionY,velocityX,velocityY,4/3*PI*radius^3*density,0)
	 * @effect	...
	 * 			|setRadius(radius)
	 * @post	...
	 * 			|this.getRadom() == new Random
	 */
	public Asteroid(double positionX, double positionY, double velocityX, double velocityY, double radius) {
		super(positionX, positionY, velocityX, velocityY, (4.0/3.0)*Math.PI*Math.pow(radius,3)*density, 0);
		setRadius(radius);
		random = new Random();
	}
	
	/**
	 * Constructor for an asteroid
	 * @param positionX
	 * @param positionY
	 * @param velocityX
	 * @param velocityY
	 * @param radius
	 * @param random
	 * 
	 * @effect	...
	 * 			|super(positionX,positionY,velocityX,velocityY,4/3*PI*radius^3*density,0)
	 * @effect 	...
	 * 			|setRadius(radius)
	 * @post	...
	 * 			|this.getRandom() == random
	 */
	public Asteroid(double positionX, double positionY, double velocityX, double velocityY, double radius, Random random) {
		super(positionX, positionY, velocityX, velocityY, (4.0/3.0)*Math.PI*Math.pow(radius,3)*density, 0);
		setRadius(radius);
		this.random = random;
	}
	
	@Basic @Raw
	public Random getRandom() {
		return random;
	}
	
	@Immutable @Basic @Raw
	public double getDensity() {
		return density;
	}
	
	/**
	 * Collide this asteroid with another flyingObject.
	 * 
	 * @param	flyingObject
	 * 			The flyingObject that will collide with this ship.
	 * @param	collisionListener
	 * 			The interface that takes care of a potential collision in the GUI.
	 * @effect	...
	 * 			| if (flyingObject instanceof Asteroid)
	 * 			|	 then super.bounceoff(flyingObject)
	 * @effect	...
	 * 			| if (flyingObject instanceof Bullet)
	 * 			|	 then
	 * 			|		 if(this.getRadius() < 30) {
	 *			|			then super.collide(flyingObject, collisionListener);
	 *			|				 collisionListener.objectCollision(this, flyingObject, this.getCollisionPosition(flyingObject)[0], this.getCollisionPosition(flyingObject)[1])
	 *			|		 else
	 *			|			let World world be this.getWorld();
	 *			|			let double direction be this.getRandom().nextInt(359)*2*Math.PI/360;
	 *			|			let double positionX1 be this.getPositionX()+(this.getRadius()/2)*Math.cos(direction);
	 *			|			let double positionY1 be this.getPositionY()+(this.getRadius()/2)*Math.sin(direction);
	 *			|			let double velocityX1 be this.getVelocity()*1.5*Math.cos(direction);
	 *			|			let double velocityY1 be this.getVelocity()*1.5*Math.sin(direction);
	 *			|			let double newRadius be this.getRadius()/2.1;
	 *			|			let double positionX2 be this.getPositionX()-(this.getRadius()/2)*Math.cos(direction);
	 *			|			let double positionY2 be this.getPositionY()-(this.getRadius()/2)*Math.sin(direction);
	 *			|			let Asteroid child1 be new Asteroid(positionX1,positionY1,velocityX1,velocityY1,newRadius);			
	 *			|			let Asteroid child2 be new Asteroid(positionX2,positionY2,-velocityX1,-velocityY1,newRadius);				
	 *			|			super.collide(flyingObject, collisionListener);
	 *			|			collisionListener.objectCollision(this, flyingObject, this.getCollisionPosition(flyingObject)[0], this.getCollisionPosition(flyingObject)[1]);
	 *			|			world.addFlyingObject(child1);
	 *			|			world.addFlyingObject(child2);
	 *@effect	...
	 *			| if (flyingObject instanceof Ship)
	 *			|	 then flyingObject.terminate()
	 *@throws	IllegalArgumentException
	 *			when the world of this ship isn't equal to the world of the flyingObject
	 *			| this.getWorld() != flyingObject.getWorld()
	 *@throws	IllegalArgumentException
	 *			when this or the flyingObject doesn't have a world
	 *			| (!this.hasWorld()) || (!flyingObject.hasWorld())
	 *@throws	NullPointerException
	 *			when flyingObject is the null-reference
	 *			| flyingObject == null
	 *@throws	IllegalStateException
	 * 			when this object is terminated
	 * 			| isTerminated()
	 */
	@Override
	public void collide(FlyingObject flyingObject, CollisionListener collisionListener) throws IllegalArgumentException, IllegalStateException, NullPointerException{
		if(isTerminated()) { throw new IllegalStateException(); }
		if(this.getWorld() != flyingObject.getWorld() || !this.hasWorld() || !flyingObject.hasWorld()) {throw new IllegalArgumentException(); }
		if(flyingObject instanceof Asteroid){
			super.bounceoff(flyingObject);
			}
			
		if(flyingObject instanceof Bullet) {
			if(this.getRadius() < 30) {
				super.collide(flyingObject, collisionListener);
				collisionListener.objectCollision(this, flyingObject, this.getCollisionPosition(flyingObject)[0], this.getCollisionPosition(flyingObject)[1]);
			}
			
			else {
				World world = this.getWorld();
				double direction = this.getRandom().nextInt(359)*2*Math.PI/360;
				double positionX1 = this.getPositionX()+(this.getRadius()/2)*Math.cos(direction);
				double positionY1 = this.getPositionY()+(this.getRadius()/2)*Math.sin(direction);
				double velocityX1 = this.getVelocity()*1.5*Math.cos(direction);
				double velocityY1 = this.getVelocity()*1.5*Math.sin(direction);
				double newRadius = this.getRadius()/2;
				double positionX2 = this.getPositionX()-(this.getRadius()/2)*Math.cos(direction);
				double positionY2 = this.getPositionY()-(this.getRadius()/2)*Math.sin(direction);
				Asteroid child1 = new Asteroid(positionX1,positionY1,velocityX1,velocityY1,newRadius);			
				Asteroid child2 = new Asteroid(positionX2,positionY2,-velocityX1,-velocityY1,newRadius);				
				super.collide(flyingObject, collisionListener);
				collisionListener.objectCollision(this, flyingObject, this.getCollisionPosition(flyingObject)[0], this.getCollisionPosition(flyingObject)[1]);
				world.addFlyingObject(child1);
				world.addFlyingObject(child2);
			}
		}
		
		if(flyingObject instanceof Ship){
			flyingObject.terminate();
		}
	}
	
}
